package priv.tuyou.dao;

import java.lang.reflect.InvocationTargetException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;

import priv.tuyou.bo.Page;
import priv.tuyou.bo.SqlParamGroup;
import priv.tuyou.convertor.DateTimeConverter;
import priv.tuyou.convertor.ReflectUtil;

public class DaoImpl implements IDao{

	@Autowired
	protected SqlSessionTemplate template;
	
	/**
	 * 表名
	 */
	protected final static String tableNameKey = "_tableName";
	
	protected final static String idKey = "_id";
	
	/**
	 * 是否删除的标志
	 */
	protected final static String deletedKey = "_deleted";
	
	/**
	 * sql参数
	 */
	protected final static String paramsKey = "_params";
	
	/**
	 * 动态查询或更新指定字段
	 */
	protected final static String fieldsKey = "_fields";
	
	/**
	 * 分页查询的开始行数、分页大小
	 */
	protected final static String startKey = "_start";
	protected final static String sizeKey = "_size";
	
	public DaoImpl(){
		
        ConvertUtils.register(new DateTimeConverter(), Date.class);
	}
	
	private <T, PK> T get(String sqlId, Class<T> c, PK id) {
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(tableNameKey, c.getSimpleName());
		map.put(idKey, id);
		
		Map<String, Object> values = template.selectOne(sqlId, map);
		if(values == null || values.isEmpty()){
			
			return null;
		}
		
		T t = null;
		try {
			t = (T)c.newInstance();
			BeanUtils.populate(t, values);
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
		return t;
	}

	public <T, PK> T get(Class<T> c, PK id) {
		
		return get("dao.get", c, id);
	}
	
	@Override
	public <T, PK> T getIgnoreDeleted(Class<T> c, PK id) {
		
		return get("dao.getIgnoreDeleted", c, id);
	}
	
	private <T, PK> Object getField(String sqlId, Class<T> t, PK id, String...fieldNames) {

		Map<String, Object> map = new HashMap<String, Object>();
		map.put(tableNameKey, t.getSimpleName());
		map.put(idKey, id);
		map.put(fieldsKey, fieldNames);
		
		Map<String, Object> result = template.selectOne(sqlId, map);
		if(result == null || result.isEmpty()){
			
			return null;
		}
		
		if(fieldNames.length == 1){
			
			return result.get(fieldNames[0]);
		}
		
		List<Object> values = new ArrayList<Object>();
		for(String field : fieldNames){
			
			values.add(result.get(field));
		}
		return values;
	}
	
	@Override
	public <T, PK> Object getField(Class<T> t, PK id, String...fieldNames) {
		
		return getField("dao.getField", t, id, fieldNames);
	}
	
	@Override
	public <T, PK> Object getFieldIgnoreDeleted(Class<T> t, PK id, String...fieldNames) {
		
		return getField("dao.getFieldIgnoreDeleted", t, id, fieldNames);
	}
	
	@Override
	public <T, PK> int delete(Class<T> c, PK id) {

		Map<String, Object> map = new HashMap<String, Object>();
		map.put(tableNameKey, c.getSimpleName());
		map.put(idKey, id);
		
		int effect = template.update("dao.delete", map);
		return effect;
	}
	
	@Override
	public <T, PK> int realDelete(Class<T> c, PK id) {
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(tableNameKey, c.getSimpleName());
		map.put(idKey, id);
		
		int effect = template.delete("dao.realDelete", map);
		return effect;
	}
	
	@Override
	public <T> int save(T t) {

		List<String> fields = new ArrayList<String>();
		List<Object> values = new ArrayList<Object>();
		
		Class<?> c = t.getClass();
		List<String> entityFields = ReflectUtil.getPropertyes(c);
		for(String pro : entityFields){
			
			fields.add(pro);
			try {
				Object value = PropertyUtils.getProperty(t, pro);
				//判断是否为java.util.Date类型，如果是要自动转换为Timestamp类型
				if(value instanceof Date){
					
					Date date = (Date)value;
					values.add(new Timestamp(date.getTime()));
				}else{
					
					values.add(value);
				}
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			}
		}
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(fieldsKey, fields);
		map.put("values", values);
		map.put(tableNameKey, c.getSimpleName());
		
		int effect = template.insert("dao.save", map);
		return effect;
	}
	
	private <T> int update(String sqlId, T t) {
		
		Map<String, Object> map = new HashMap<String, Object>();
		Map<String, Object> fields = new HashMap<String, Object>();
		map.put(tableNameKey, t.getClass().getSimpleName());
		map.put(fieldsKey, fields);
		
		Class<?> c = t.getClass();
		List<String> entityFields = ReflectUtil.getPropertyes(c);
		for(String pro : entityFields){
			
			Object value = null;
			try {
				value = PropertyUtils.getProperty(t, pro);
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			}
			
			//判断是否为java.util.Date类型，如果是要自动转换为Timestamp类型
			if(value instanceof Date){
				
				Date date = (Date)value;
				Timestamp timestamp = new Timestamp(date.getTime());
				if(idKey.equals(pro)){
					
					map.put(pro, timestamp);
				}else{
					
					fields.put(pro, timestamp);
				}
			}else{
				
				if(idKey.equals(pro)){
					
					map.put(pro, value);
				}else{
					
					fields.put(pro, value);
				}
			}
		}
		int effect = template.update(sqlId, map);
		return effect;
	}
	
	@Override
	public <T> int update(T t) {
		
		return update("dao.update", t);
	}
	
	@Override
	public <T> int updateIgnoreDeleted(T t) {
		
		return update("dao.updateIgnoreDeleted", t);
	}
	
	private <T, PK> int update(String sqlId, Class<T> c, PK id, Map<String, Object> map) {
		
		Map<String, Object> newMap = new HashMap<String, Object>();
		newMap.put(tableNameKey, c.getSimpleName());
		newMap.put(idKey, id);
		newMap.put(paramsKey, map);
		
		Map<String, Object> fields = new HashMap<String, Object>();
		for(String pro : map.keySet()){
			
			Object value = map.get(pro);
			
			//判断是否为java.util.Date类型，如果是要自动转换为Timestamp类型
			if(value instanceof Date){
				
				Date date = (Date)value;
				Timestamp timestamp = new Timestamp(date.getTime());
				fields.put(pro, timestamp);
			}else{
				
				fields.put(pro, value);
			}
		}
		
		int effect = template.update(sqlId, newMap);
		return effect;
	}
	
	@Override
	public <T, PK> int update(Class<T> c, PK id, Map<String, Object> map) {
		
		return update("dao.updateApart", c, id, map);
	}
	
	@Override
	public <T, PK> int updateIgnoreDeleted(Class<T> c, PK id, Map<String, Object> map) {
		
		return update("dao.updateIgnoreDeleted", c, id, map);
	}
	
	private <T> List<T> queryAll(String sqlId, Class<T> c){
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(tableNameKey, c.getSimpleName());
		
		List<Map<String, Object>> values = template.selectList(sqlId, map);
		List<T> list = new ArrayList<T>(values.size());
		for(Map<String, Object> obj : values){
			
			try {
				T t = c.newInstance();
				BeanUtils.populate(t, obj);
				list.add(t);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		return list;
	}
	
	@Override
	public <T> List<T> queryAll(Class<T> c) {
		
		return queryAll("dao.queryAll", c);
	}
	
	@Override
	public <T> List<T> queryAllIgnoreDeleted(Class<T> c) {
		
		return queryAll("dao.queryAllIgnoreDeleted", c);
	}
	
	private <T> List<T> query(String sqlId, Class<T> c, SqlParamGroup params) {
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(tableNameKey, c.getSimpleName());
		map.put(paramsKey, params.toString());
		map.putAll(params.getParamArray());
		
		List<Map<String, Object>> values = template.selectList(sqlId, map);
		List<T> list = new ArrayList<T>(values.size());
		for(Map<String, Object> obj : values){
			
			try {
				T t = c.newInstance();
				BeanUtils.populate(t, obj);
				list.add(t);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		return list;
	}
	
	@Override
	public <T> List<T> query(Class<T> c, SqlParamGroup params) {
		
		return query("dao.query", c, params);
	}
	
	@Override
	public <T> List<T> queryIgnoreDeleted(Class<T> c, SqlParamGroup params) {

		return query("dao.queryIgnoreDeleted", c, params);
	}
	
	private <T> Object query(String sqlId, Class<T> c, SqlParamGroup params, String... fieldNames){
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(tableNameKey, c.getSimpleName());
		map.put(fieldsKey, fieldNames);
		map.put(paramsKey, params.toString());
		map.putAll(params.getParamArray());
		
		List<Map<String, Object>> values = template.selectList(sqlId, map);
		if(values.size() == 1){
			
			if(fieldNames.length == 1){
				
				return values.get(0).get(fieldNames[0]);
			}else{
				
				return values.get(0);
			}
		}else if(fieldNames.length == 1){
			
			List<Object> list = new ArrayList<Object>(values.size());
			for(Map<String, Object> item : values){
				
				list.add(item.get(fieldNames[0]));
			}
			return list;
		}
		return values;
	}
	
	@Override
	public <T> Object query(Class<T> c, SqlParamGroup params, String... fieldNames) {

		return query("dao.queryField", c, params, fieldNames);
	}
	
	@Override
	public <T> Object queryIgnoreDeleted(Class<T> c, SqlParamGroup params, String... fieldNames) {

		return query("dao.queryFieldIgnoreDeleted", c, params, fieldNames);
	}
	
	private <T> long count(String sqlId, Class<T> c){
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(tableNameKey, c.getSimpleName());
		
		long count = template.selectOne(sqlId, map);
		return count;
	}
	
	@Override
	public <T> long count(Class<T> c) {

		return count("dao.count", c);
	}
	
	@Override
	public <T> long countIgnoreDeleted(Class<T> c) {

		return count("dao.countIgnoreDeleted", c);
	}
	
	private <T> long count(String sqlId, Class<T> c, SqlParamGroup params){
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(tableNameKey, c.getSimpleName());
		map.put(paramsKey, params.toString());
		map.putAll(params.getParamArray());
		
		long count = template.selectOne(sqlId, map);
		return count;
	}
	
	@Override
	public <T> long count(Class<T> c, SqlParamGroup params) {

		return count("dao.countByCondition", c, params);
	}
	
	@Override
	public <T> long countIgnoreDeleted(Class<T> c, SqlParamGroup params) {

		return count("dao.countByConditionIgnoreDeleted", c, params);
	}
	
	private <T> void pageQuery(String sqlId, Class<T> c, Page<T> page){
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(tableNameKey, c.getSimpleName());
		map.put(startKey, page.getPageStart());
		map.put(sizeKey, page.getPageSize());
		
		List<Map<String, Object>> values = template.selectList(sqlId, map);
		List<T> list = new ArrayList<T>(values.size());
		for(Map<String, Object> obj : values){
			
			try {
				T t = c.newInstance();
				BeanUtils.populate(t, obj);
				list.add(t);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		page.setData(list);
	}
	
	@Override
	public <T> void pageQuery(Class<T> c, Page<T> page) {

		long totalCount = count(c);
		page.setTotalCount(totalCount);
		pageQuery("dao.pageQuery", c, page);
	}
	
	@Override
	public <T> void pageQueryIgnoreDeleted(Class<T> c, Page<T> page) {

		long totalCount = count(c);
		page.setTotalCount(totalCount);
		pageQuery("dao.pageQueryIgnoreDeleted", c, page);
	}
	
	private <T> void pageQuery(String sqlId, Class<T> c, Page<T> page, SqlParamGroup params) {
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(tableNameKey, c.getSimpleName());
		map.put(startKey, page.getPageStart());
		map.put(sizeKey, page.getPageSize());
		map.put(paramsKey, params.toString());
		map.putAll(params.getParamArray());
		
		List<Map<String, Object>> values = template.selectList(sqlId, map);
		List<T> list = new ArrayList<T>(values.size());
		for(Map<String, Object> obj : values){
			
			try {
				T t = c.newInstance();
				BeanUtils.populate(t, obj);
				list.add(t);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		page.setData(list);
	}
	
	@Override
	public <T> void pageQuery(Class<T> c, Page<T> page, SqlParamGroup params) {

		long totalCount = count(c, params);
		page.setTotalCount(totalCount);
		pageQuery("dao.pageQueryByCondition", c, page, params);
	}
	
	@Override
	public <T> void pageQueryIgnoreDeleted(Class<T> c, Page<T> page, SqlParamGroup params) {

		long totalCount = count(c, params);
		page.setTotalCount(totalCount);
		pageQuery("dao.pageQueryByConditionIgnoreDeleted", c, page, params);
	}

	public SqlSessionTemplate getTemplate() {
		return template;
	}

	public void setTemplate(SqlSessionTemplate template) {
		this.template = template;
	}
}
